#ifndef _EPOLL_H_2016_1221_
#define _EPOLL_H_2016_1221_
/*
* epoll
*
*/

#include "../platform/_platform_def.h"
#include "net.h"
#ifndef PLATFORM_WIN32
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/epoll.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <math.h>
#include <iostream>
#include <sys/resource.h>
#define MAX_OPENFILE 100000
class netepoll:public inetselctor
{
public:
	netepoll(io_handler* parent,std::string ip,unsigned int port):_io_hander(parent),_status(NS_WAIT),_exit(false),_fd(-1),_ip(ip),_port(port)
	{

	}
	virtual ~CNetSelect()
	{
		_status =NS_EXIT;
		_exit = true;
	}
public:
	virtual int start()
	{
		_status = NS_RUN;
		struct epoll_event ev, evs[MAX_EVENTS];
		for (; ; )
		{
			int count = epoll_wait(epfd, evs, MAX_EVENTS, 1000);
			if (count < 0)
			{
				LOG4CXX_FATAL(logger_, "epoll_wait error!");
			}
			time_t now = Clock::getCurrentSystemTime();
// 			if (!preNetEvent(now))
// 			{
// 				LOG4CXX_ERROR(logger_, "PreNetEvent returned false, terminating...");
// 				break;
// 			}
			for (int i = 0; i < count; i++)
			{
				int fd = evs[i].data.fd;
				if (islistensock(fd))
				{
					struct sockaddr_in sa;
					socklen_t slen = sizeof (sa);
					int nfd = 0;
					nfd = accept(fd, (struct sockaddr*) &sa, &slen);
					if (nfd > 0)
					{
						ev.events = EPOLLIN | EPOLLHUP; //| EPOLLRDHUP;
						ev.data.fd = nfd;
						if (epoll_ctl(epfd, EPOLL_CTL_ADD, nfd, &ev) < 0)
						{
							//LOG4CXX_ERROR(logger_, "epoll_ctl error, cannot add client!");
						}
						else
						{
							_router->add_to_fd_cache(fd,RT_CLIENT);

//								size_t rsize = readCacheSize(fd);
// 							//size_t rsize = (fd==wsfd ? NetCache::WEBSERVER_READ_SIZE : NetCache::DEFAULT_READ_SIZE);
// 							NetCache *cache = addConnection(nfd, sa, rsize);
// 							createProtocolHandler(cache, fd);
						}
					}
				}
				else // data
				{
					netcache* cache = _router->get_cache_from_fd(i,RT_CLIENT);
					if(!cache)
						continue;
					
					if (cache != NULL)
					{
						__uint32_t events = evs[i].events;
						bool readError = false;
						if ((events & EPOLLIN) > 0)
						{
							int64 uid = cache->uid;
							readError = !cache->get();
							if (!readError)
							{
								string req;
								while (cache->assemble(req) && !cache->remove)
								{
									//LOG4CXX_DEBUG(logger_, "Command Received: \"" << req.c_str() << "\" from uid:" << uid << " fd:" << fd);

									if (cache->ph != NULL)
									{
										cache->ph->handle(uid, req);
									}
									else
									{
										//LOG4CXX_ERROR(logger_, "Protocol handler is NULL for fd:" << fd);
									}
								}
							}
						}
						if ((events & EPOLLOUT) > 0)
						{
							if ( isConnecting( fd ))
							{
								connectSuccess( fd );
							}
							else if (cache->write())
							{
								ev.events = EPOLLIN | EPOLLHUP; // | EPOLLRDHUP;
								ev.data.fd = fd;
								epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
							}
						}
						if ((cache->remove && !cache->waitToWrite()) || readError ||
							(events & EPOLLHUP) > 0 || //(events&EPOLLRDHUP)>0 ||
							(events & EPOLLERR) > 0)
						{
							int64 uid = cache->uid;
							if (uid >= 0 && cache->ph != NULL)
							{
								cache->ph->leave(uid);
							}
							doCloseConnection(fd);
							if (isConnectSocket(fd))
							{
								connectFailed(fd);
							}
						}
					}
					else
					{
					}
				}
			}
		}
		close_socket(_fd);
		//_exit(-1);
	}
	virtual void exit()
	{
		_status =NS_EXIT;
		_exit = true;
	}
	virtual void stop()
	{
		_status =NS_STOP;
	}
private:
	int init()
	{
		struct rlimit srl;
		srl.rlim_cur = MAX_OPENFILE + 10;
		srl.rlim_max = MAX_OPENFILE + 10;
		if (setrlimit(RLIMIT_NOFILE, &srl) < 0)
		{
			return -1;
		}
		//block sig pipe
		struct sigaction sa;
		memset(&sa, 0, sizeof (sa));
		sa.sa_handler = SIG_IGN;
		if (sigaction(SIGPIPE, &sa, NULL) < 0)
		{
			return -2;
		}
	}
	void closesocket(SY_SOCKET fd)
	{
		if (fd > 0)
		{
			struct epoll_event ev;
			epoll_ctl(epfd, EPOLL_CTL_DEL, fd, &ev);
			shutdown(fd, SHUT_RDWR);
			close(fd);
			//removeConnection(fd);
		}


	}
	void initlistensock(const char* addr,const char* port)
	{
		int ret = socket(PF_INET, SOCK_STREAM, 0);
		if (ret < 0)
		{
		}
		int yes = 1;
		if (setsockopt(ret, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int) ) < 0)
		{
		}

		struct addrinfo hints, *res;
		memset(&hints, 0, sizeof (hints));
		hints.ai_family = AF_INET;
		hints.ai_socktype = SOCK_STREAM;
		hints.ai_flags = AI_PASSIVE;

		getaddrinfo(addr, port, &hints, &res);

		if (bind(ret, res->ai_addr, res->ai_addrlen) < 0)
		{
			LOG4CXX_FATAL(logger_, "bind error for " + socket_name);
			exit(1);
		}

		freeaddrinfo(res);

		if (listen(ret, 1000) < 0)
		{
			LOG4CXX_FATAL(logger_, "listen error for " + socket_name);
			exit(1);
		}
		LOG4CXX_INFO(logger_, "Listening " + socket_name + " on port:" + port);

		struct epoll_event ev;
		ev.events = EPOLLIN;
		ev.data.fd = ret;
		if (epoll_ctl(epfd, EPOLL_CTL_ADD, ret, &ev) < 0)
		{
			exit(1);
		}
	}
	bool senddata()
	{
		NetCache *cache = getCacheFromFd(fd);
		if (cache == NULL)
		{
			return false;
		}
		if (!cache->prepareWrite(str, size))
		{
			LOG4CXX_ERROR(logger_,"prepareWrite Error!!!! fd:"<<fd<<"size:"<<size);
			return false;
		}
		else
		{
			struct epoll_event ev;
			ev.events = EPOLLIN | EPOLLOUT | EPOLLHUP; // | EPOLLRDHUP;
			ev.data.fd = fd;
			epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev);
			return true;
		}

	}
private:
	io_handler* _io_hander;
	NET_STAT _status;
	bool _exit;
	SY_SOCKET _fd;
	std::string _ip;
	int _port;
};

#endif// PLATFORM_WIN32
#endif //_EPOLL_H_2016_1221_